using System;
using System.Collections.Generic;
using System.Text;

namespace GrooveWebServicesV12Helpers.InfoPathForms
{
    internal class XMLHelper
    {   
        private const string NAMESPACE_QUERY = "namespace::*";
        private const string PI_QUERY = "//processing-instruction('{0}')";
        private const string ATTRIBUTE_QUERY = "{0}=\"(?'{1}'[^\"]*)\"";
        private const string ATTRIBUTE_VALUE_MATCH_NAME = "AttributeValue";
    
        /// <summary>
        /// Creates and populates a namespace manager based on the namespace information contained
        /// within the document.  It is necessary to do this before performing any XPath queries
        /// against an XML document.
        /// </summary>
        /// <param name="i_Doc">Document for which to create an XmlNamespaceManager</param>
        /// <returns>XmlNamespaceManager for i_Doc</returns>
        static internal System.Xml.XmlNamespaceManager CreateNamespaceMgr(System.Xml.XmlDocument i_Doc)
        {
            // Fill up the namespace manager so that we can perform XPath queries against the document:
            System.Xml.XmlNodeList namespaceNodes = i_Doc.DocumentElement.SelectNodes(NAMESPACE_QUERY);
            System.Xml.XmlNamespaceManager retVal = new System.Xml.XmlNamespaceManager(i_Doc.NameTable);
            for (int i = 0; i < namespaceNodes.Count; i++)
            {
                System.Xml.XmlNode namespaceNode = namespaceNodes[i];
                retVal.AddNamespace(namespaceNode.LocalName, namespaceNode.Value);
            }

            return retVal;
        }

        /// <summary>
        /// Replaces an attribute value of a processing instruction within a given document.  For
        /// example, passing in an InfoPath data document, processing instruction name of 
        /// mso-infoPathSolution, attribute name of href, will cause this routine to update the
        /// data document in a manner similar to the Microsoft Office InfoPath 2003 developer tool 
        /// pifix.exe using the /URL switch.
        /// </summary>
        /// <param name="io_Doc">Document to replace attribute value in.</param>
        /// <param name="i_ProcessingInstructionName">Processing instruction containing the attribute.</param>
        /// <param name="i_AttributeName">Name of the attribute whose value is to be replaced.</param>
        /// <param name="i_AttributeValue">New attribute value.</param>
        static internal void SetProcessingInstructionAttributeValue(System.Xml.XmlDocument io_Doc, string i_ProcessingInstructionName, string i_AttributeName, string i_AttributeValue)
        {
            // Create a namespace manager for the document:
            System.Xml.XmlNamespaceManager nsMgr = CreateNamespaceMgr(io_Doc);

            // Get the processing instruction node:
            string queryPI = string.Format(PI_QUERY, i_ProcessingInstructionName);
            System.Xml.XmlNode PINode = io_Doc.DocumentElement.SelectSingleNode(queryPI, nsMgr);
            System.Xml.XmlProcessingInstruction PI = (System.Xml.XmlProcessingInstruction)PINode;

            // We'll use a simple regular expression to get the current value of the 
            // attribute.  We do this because although we can issue a simple XPath to get the
            // processing instruction, there is not an attribute collection associated with
            // the PI node.  PI nodes have only a Data property which makes us have to
            // parse the string for the value we are looking for:
            string attributeValRegEx = string.Format(ATTRIBUTE_QUERY, i_AttributeName, ATTRIBUTE_VALUE_MATCH_NAME);
            System.Text.RegularExpressions.Regex regEx = new System.Text.RegularExpressions.Regex
                (attributeValRegEx, System.Text.RegularExpressions.RegexOptions.ExplicitCapture);
            System.Text.RegularExpressions.Match regExMatch = regEx.Match(PI.Data);

            // Only continue if we have found a match.  The group count here must be two.
            // The first group is the entire match including the "[AttributeName]=" portion 
            // of the string.  The second group will be the value of the attribute.  This is 
            // the piece we need to replace with the new value.
            if ((regExMatch.Success) && (regExMatch.Groups.Count == 2))
            {
                // Get the group, remove the current contents from the processing instruction,
                // and add the new value:
                System.Text.RegularExpressions.Group matchGroup = regExMatch.Groups[ATTRIBUTE_VALUE_MATCH_NAME];
                PI.Data = PI.Data.Remove(matchGroup.Index, matchGroup.Length);
                PI.Data = PI.Data.Insert(matchGroup.Index, i_AttributeValue);
            }
        }

        static internal string GetProcessingInstructionAttributeValue(System.Xml.XmlDocument i_Doc, string i_ProcessingInstructionName, string i_AttributeName)
        {
            string attributeValue = null;

            // Create a namespace manager for the document:
            System.Xml.XmlNamespaceManager nsMgr = CreateNamespaceMgr(i_Doc);

            // Get the processing instruction node:
            string queryPI = string.Format(PI_QUERY, i_ProcessingInstructionName);
            System.Xml.XmlNode PINode = i_Doc.DocumentElement.SelectSingleNode(queryPI, nsMgr);
            System.Xml.XmlProcessingInstruction PI = (System.Xml.XmlProcessingInstruction)PINode;

            // We'll use a simple regular expression to get the current value of the 
            // attribute.  We do this because although we can issue a simple XPath to get the
            // processing instruction, there is not an attribute collection associated with
            // the PI node.  PI nodes have only a Data property which makes us have to
            // parse the string for the value we are looking for:
            string attributeValRegEx = string.Format(ATTRIBUTE_QUERY, i_AttributeName, ATTRIBUTE_VALUE_MATCH_NAME);
            System.Text.RegularExpressions.Regex regEx = new System.Text.RegularExpressions.Regex
                (attributeValRegEx, System.Text.RegularExpressions.RegexOptions.ExplicitCapture);
            System.Text.RegularExpressions.Match regExMatch = regEx.Match(PI.Data);

            // Only continue if we have found a match.  The group count here must be two.
            // The first group is the entire match including the "[AttributeName]=" portion 
            // of the string.  The second group will be the value of the attribute.  This is 
            // the piece we need to replace with the new value.
            if ((regExMatch.Success) && (regExMatch.Groups.Count == 2))
            {
                // Get the group, remove the current contents from the processing instruction,
                // and add the new value:
                System.Text.RegularExpressions.Group matchGroup = regExMatch.Groups[ATTRIBUTE_VALUE_MATCH_NAME];
                attributeValue = PI.Data.Substring(matchGroup.Index, matchGroup.Length);
            }

            return attributeValue;
        }
    }
}
